package com.xci.platform.auth.service;

import cn.hutool.core.convert.Convert;
import com.xci.platform.annotation.OperateUser;
import com.xci.platform.auth.core.AuthConstant;
import com.xci.platform.auth.core.AuthUser;
import com.xci.platform.auth.core.HistoryModel;
import com.xci.platform.auth.core.HistoryOperateType;
import com.xci.platform.auth.dao.DepartmentDao;
import com.xci.platform.auth.dao.RoleDao;
import com.xci.platform.auth.dao.UserDao;
import com.xci.platform.auth.entity.DepartmentEntity;
import com.xci.platform.core.*;
import com.xci.platform.helper.CoreHelper;
import com.xci.platform.helper.ObjectHelper;
import com.xci.platform.helper.StringHelper;
import com.xci.platform.helper.TreeHelper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 系统部门服务
 */
@SuppressWarnings("unchecked")
@Service
public class DepartmentService extends AuthBaseService {

    /**
     * 部门数据访问对象
     */
    @Resource
    private DepartmentDao departmentDao;

    /**
     * 用户数据访问对象
     */
    @Resource
    private UserDao userDao;

    /**
     * 角色数据访问对象
     */
    @Resource
    private RoleDao roleDao;

    /**
     * 树形结构操作服务对象
     */
    private TreeOperateService treeOperateService;

    /**
     * 获取树形结构操作服务对象
     */
    public TreeOperateService getTreeOperateService() {
        if (treeOperateService == null) {
            treeOperateService = new TreeOperateService(departmentDao);
        }
        return treeOperateService;
    }

    /**
     * 保存部门
     *
     * @param entity   部门对象
     * @param isCreate 是否新建
     */
    @Validated
    @OperateUser
    @Transactional(rollbackFor = Exception.class)
    public BoolMessage save(DepartmentEntity entity, Boolean isCreate) {
        //添加名称简拼
        entity.setSpell(StringHelper.getSpell(entity.getName()));
        //验证父节点
        if (StringHelper.isBlank(entity.getParentId())) {
            entity.setParentId(Constant.RootNodeId);
        }
        //部门名称验证
        if (departmentDao.existByName(entity.getName(), entity.getId()) > 0) {
            String msg = StringHelper.format("部门名称 {} 已经存在", entity.getName());
            return new BoolMessage(false, msg);
        }

        String msg;
        long startTime = CoreHelper.startWatch();
        if (isCreate) {
            entity.setPath(getTreeOperateService().buildNewPath(entity.getParentId()));//获取路径
            msg = StringHelper.format("新增部门 {}", entity.getName());
            departmentDao.insert(entity);
            //region 历史记录
            operateHistory(buildHistory()
                    .operateType(HistoryOperateType.Insert)
                    .keyValue(entity.getId())
                    .message(msg)
                    .after(entity)
            );
            //endregion
        } else {
            msg = StringHelper.format("修改部门 {}", entity.getName());
            DepartmentEntity oldEntity = queryById(entity.getId());
            departmentDao.update(entity);
            //部门名称发生变化时,更新用户表和角色表中的部门名称字段
            if (!oldEntity.getName().equals(entity.getName())) {
                userDao.updateDeptNameByDeptId(entity.getId(), entity.getName());
                roleDao.updateDeptNameByDeptId(entity.getId(), entity.getName());
            }
            //region 历史记录
            operateHistory(buildHistory()
                    .operateType(HistoryOperateType.Update)
                    .keyValue(entity.getId())
                    .message(msg)
                    .before(oldEntity)
                    .after(entity)
            );
            //endregion
        }
        // 操作日志
        operateLog(AuthConstant.SystemModule.Department, msg, CoreHelper.stopWatch(startTime));
        //更新节点叶子状态
        getTreeOperateService().updateLeafStatus();
        return BoolMessage.True;
    }

    /**
     * 删除部门
     *
     * @param id       部门主键
     * @param authUser 当前登录
     */
    @Validated
    @Transactional(rollbackFor = Exception.class)
    public BoolMessage delete(@NotEmpty(message = "请指定删除的部门主键") String id, AuthUser authUser) {
        BoolMessage result = BoolMessage.True;
        DepartmentEntity currentEntity = departmentDao.queryById(id);
        if (currentEntity == null) return BoolMessage.fail("无效的部门主键");
        // 检测角色表是否有引用
        if (roleDao.existByDeptId(currentEntity.getId()) > 0) {
            //角色表中引用了此部门,不允许删除
            return BoolMessage.fail("系统角色中已经引用了当前部门,无法删除!");
        }
        // 检测用户表是否有引用
        if (userDao.existByDeptId(currentEntity.getId()) > 0) {
            //用户表中引用了此部门,不允许删除
            return BoolMessage.fail("系统用户中已经引用了当前部门,无法删除!");
        }

        // 检测当前账号所属部门是否是自身,不允许自己删除自己的部门
        if (currentEntity.getId().equals(authUser.getDepartmentId())) {
            return BoolMessage.fail("不允许删除自身所在的部门");
        }

        List<DepartmentEntity> childs = (List<DepartmentEntity>) TreeHelper.getChilds(departmentDao.query(), id, true);
        for (DepartmentEntity entity : childs) {
            long startTime = CoreHelper.startWatch();
            departmentDao.delete(entity.getId());
            String msg = StringHelper.format("删除部门 {}", entity.getName());
            //region 历史记录
            operateHistory(buildHistory()
                    .operateType(HistoryOperateType.Delete)
                    .keyValue(entity.getId())
                    .message(msg)
                    .before(entity)
            );
            //endregion
            operateLog(AuthConstant.SystemModule.Department, msg, CoreHelper.stopWatch(startTime)); // 操作日志
        }
        //更新节点叶子状态
        getTreeOperateService().updateLeafStatus();
        return BoolMessage.True;
    }

    /**
     * 保存拖拽数据(父节点和路径)
     *
     * @param nodeIndexs 更新的节点数据
     */
    @Transactional(rollbackFor = Exception.class)
    public BoolMessage updateNodeIndex(TreeNodeIndex[] nodeIndexs) {
        getTreeOperateService().updateParentAndPath(nodeIndexs);
        getTreeOperateService().rebuildPath(Constant.RootNodeId);
        return BoolMessage.True;
    }

    /**
     * 更新部门启用状态
     *
     * @param id     部门主键
     * @param status 是否启用(1:启用 0:禁用)
     */
    @Validated
    @Transactional(rollbackFor = Exception.class)
    public BoolMessage updateStatus(@NotNull(message = "请指定更新状态的部门主键") String id,
                                    @NotNull(message = "请指定状态") Integer status) {
        long startTime = CoreHelper.startWatch();
        departmentDao.updateStatus(id, status);
        // 操作日志
        String msg = StringHelper.format("更新部门状态 主键: {} 更新后状态: {}", id, status);
        operateLog(AuthConstant.SystemModule.Department, msg, CoreHelper.stopWatch(startTime));
        return BoolMessage.True;
    }

    /**
     * 根据部门主键获取部门对象
     *
     * @param id 部门主键
     * @return 返回指定部门主键的部门对象
     */
    @Validated
    public DepartmentEntity queryById(@NotNull(message = "请指定部门主键") String id) {
        return departmentDao.queryById(id);
    }


    /**
     * 根据部门主键获取子节点列表
     *
     * @param id        部门主键
     * @param allColumn 是否查询所有列:true 查询所有列,false 只查询id
     * @param allChild  是否查询所有子节点:true 查询所有的子孙节点,false 只查询一层子节点
     * @param self      是否包含自身
     */
    public List<DepartmentEntity> queryChild(@NotNull(message = "请指定部门主键") String id, boolean allColumn, boolean allChild, boolean self) {
        String path = departmentDao.queryPathById(id);
        Assert.hasText(path, "指定的部门路径无效");
        return departmentDao.queryChild(id, path, allColumn, allChild, self);
    }

    /**
     * 查询部门列表
     *
     * @return 返回符合查询条件的部门列表
     */
    public List<DepartmentEntity> query(Map params) {
        if (params == null) params = new HashMap();
        Object nameObj = params.get("name");
        Object statusObj = params.get("status");
        List<DepartmentEntity> list = departmentDao.query();
        if (ObjectHelper.isBlank(nameObj) && ObjectHelper.isBlank(statusObj)) {
            return list;
        }
        List<DepartmentEntity> result = list;
        if (ObjectHelper.isNotBlank(nameObj)) {
            String name = nameObj.toString().toLowerCase();
            result = result.stream().filter(p -> p.getName().toLowerCase().contains(name)
                    || (p.getSpell() != null && p.getSpell().toLowerCase().contains(name)))
                    .collect(Collectors.toList());
        }
        if (ObjectHelper.isNotBlank(statusObj)) {
            result = result.stream().filter(p -> p.getStatus().equals(Convert.toInt(statusObj))).collect(Collectors.toList());
        }
        return (List<DepartmentEntity>) TreeHelper.filter(list, result);
    }

    /**
     * 根据机构主键集合查询部门列表
     *
     * @return 返回符合查询条件的部门列表
     */
    public List<DepartmentEntity> queryByIds(List<String> deptIds) {
        return departmentDao.queryByIds(deptIds);
    }

    /**
     * 动态加载部门列表
     *
     * @param id 部门主键
     * @return 部门列表
     */
    public List<DepartmentEntity> dynamicQuery(String id) {
        return departmentDao.dynamicQuery(id);
    }

    /**
     * 获取部门名称
     *
     * @param id 部门主键
     * @return 部门名称
     */
    public String queryNameById(String id) {
        return departmentDao.queryNameById(id);
    }

    /**
     * 根据部门主键集合查询部门名称,并把多个名称用逗号隔开
     * @param idList 部门主键集合
     */
    public String queryNameStringByIdList(List<String> idList) {
        if (ObjectUtils.isEmpty(idList)) {
            return Constant.Empty;
        }
        List<DepartmentEntity> deptList = departmentDao.queryByIds(idList);
        String deptNames = deptList.stream().map(DepartmentEntity::getName).collect(Collectors.joining(","));
        return deptNames;
    }

    /**
     * 转为节点列表
     *
     * @param modelList 模型列表
     */
    public List<TreeNode> convertToNodeList(List<DepartmentEntity> modelList) {
        return TreeHelper.toTreeNodeList(modelList);
    }

    /**
     * 生成历史记录对象
     */
    private HistoryModel.HistoryModelBuilder buildHistory() {
        return HistoryModel.builder()
                .tableName("sys_department")
                .tableCaption("系统部门")
                .keyName("id");
    }
}
